// 引入 React 相关的 hooks
import React, { useState, useRef, useEffect } from "react";
// 引入 pdfjs-dist 库，用于处理 PDF 文件
import * as pdfjsLib from "pdfjs-dist";
// 引入 pdfjs-dist 的 CSS 文件
import "pdfjs-dist/web/pdf_viewer.css";
// 引入自定义的 CSS 文件
import "./PdfViewer.css";

// 设置 pdfjsLib 的全局 worker 源
pdfjsLib.GlobalWorkerOptions.workerSrc = `./pdf.worker.mjs`;

/**
 * 定义聊天消息的接口
 * @interface ChatMessage
 * @property {'user' | 'ai' | 'pdf'} type - 消息类型，可能是用户消息、AI 消息或 PDF 消息
 * @property {string} content - 消息内容
 * @property {File} [pdfFile] - 可选的 PDF 文件
 */
interface ChatMessage {
  type: "user" | "ai" | "pdf";
  content: string;
  pdfFile?: File;
}

/**
 * PdfViewer 组件，用于显示聊天界面和 PDF 预览
 * @returns {JSX.Element} 渲染的组件
 */
const PdfViewer: React.FC = () => {
  // 定义消息状态，初始包含一些示例消息
  const [messages, setMessages] = useState<ChatMessage[]>([
    {
      type: "ai",
      content: "你好！我是AI助手，可以帮你分析PDF文档，请上传文件或直接提问。",
    },
    {
      type: "user",
      content: "帮我分析一下这份简历",
    },
    {
      type: "pdf",
      content: "前端开发-大伟.pdf",
      pdfFile: undefined, // 修改这里，使用 undefined 替代 null
    },
    {
      type: "ai",
      content: `根据PDF内容分析，这是一份前端开发工程师的简历，主要特点如下：

1. 技术栈：
- 熟练掌握 Vue/React 全家桶
- 熟悉 Event loop、浏览器渲染原理
- 对 React 架构有深入研究，包括 Reconciler、Fiber 等
- 有微信小程序开发经验

2. 工作经历：
- 目前就职于杭州某科技公司
- 负责前端项目开发和性能优化
- 参与多个项目的技术选型和架构设计

3. 项目经验：
- 负责公司核心业务系统的开发
- 实现了复杂的数据可视化需求
- 解决了多个性能优化问题

建议可以进一步了解该候选人的：
1. 具体项目难点攻克经历
2. 团队协作能力
3. 技术深度理解程度`,
    },
    {
      type: "user",
      content: "帮我总结一下他的主要技术特长",
    },
    {
      type: "ai",
      content: `该候选人的主要技术特长包括：

1. 前端框架：
- Vue.js 和 React 技术栈深度应用
- 对框架底层原理有研究
- 具备组件库开发经验

2. 性能优化：
- 了解浏览器渲染机制
- 掌握前端性能优化方法
- 有实际的优化案例

3. 工程化能力：
- 熟悉 Webpack 构建流程
- 了解前端工程化最佳实践
- 具备项目架构设计能力

4. 跨平台开发：
- 有小程序开发经验
- 了解混合开发技术

整体来看，技术栈比较全面，且对技术有一定深度的理解。`,
    },
  ]);
  // 定义输入消息状态
  const [inputMessage, setInputMessage] = useState("");
  // 定义选中文本状态
  const [selectedText, setSelectedText] = useState<string>("");
  // 定义 AI 解释文本状态
  const [explanation, setExplanation] = useState<string>("");
  // 定义是否显示悬浮框状态
  const [showFloatingBox, setShowFloatingBox] = useState<boolean>(false);
  // 定义悬浮框位置状态
  const [floatingBoxPosition, setFloatingBoxPosition] = useState<{
    top: number;
    left: number;
  }>({ top: 0, left: 0 });
  // 定义当前 PDF 文件状态
  const [currentPdfFile, setCurrentPdfFile] = useState<File | null>(null);

  // 创建对 PDF 查看器 DOM 元素的引用
  const pdfViewerRef = useRef<HTMLDivElement>(null);
  // 创建对悬浮框 DOM 元素的引用
  const floatingBoxRef = useRef<HTMLDivElement>(null);
  // 创建对聊天容器 DOM 元素的引用
  const chatContainerRef = useRef<HTMLDivElement>(null);

  /**
   * 处理消息发送的函数
   * @async
   * @returns {Promise<void>}
   */
  const handleSendMessage = async () => {
    // 检查输入消息是否为空
    if (inputMessage.trim()) {
      // 添加用户消息到消息列表
      setMessages((prev) => [...prev, { type: "user", content: inputMessage }]);

      try {
        // 调用 AI API 获取回复
        const response = await fetch("/api/chat", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ message: inputMessage }),
        });
        const data = await response.json();

        // 添加 AI 回复到消息列表
        setMessages((prev) => [
          ...prev,
          { type: "ai", content: data.response },
        ]);
      } catch (error) {
        // 打印发送消息失败的错误信息
        console.error("发送消息失败:", error);
      }

      // 清空输入框
      setInputMessage("");
    }
  };

  /**
   * 处理 PDF 文件上传的函数
   * @param {File} file - 上传的 PDF 文件
   * @returns {void}
   */
  const handlePdfUpload = (file: File) => {
    // 设置当前 PDF 文件
    setCurrentPdfFile(file);
    // 添加 PDF 消息到消息列表
    setMessages((prev) => [
      ...prev,
      {
        type: "pdf",
        content: `已上传PDF文件：${file.name}`,
        pdfFile: file,
      },
    ]);
    // 加载并渲染 PDF 文件
    loadPDF(file);
  };

  /**
   * 加载并渲染 PDF 文件的函数
   * @param {File} file - 要加载的 PDF 文件
   * @returns {void}
   */
  const loadPDF = (file: File) => {
    // 创建文件读取器
    const fileReader = new FileReader();
    // 当文件读取完成时执行的回调函数
    fileReader.onload = () => {
      // 将文件读取结果转换为 Uint8Array 类型
      const typedArray = new Uint8Array(fileReader.result as ArrayBuffer);
      // 渲染 PDF 文件
      renderPDF(typedArray);
    };
    // 以二进制数据形式读取文件
    fileReader.readAsArrayBuffer(file);
  };

  /**
   * 使用 PDF.js 渲染 PDF 为 HTML 的函数
   * @async
   * @param {Uint8Array} data - PDF 文件的二进制数据
   * @returns {Promise<void>}
   */
  const renderPDF = async (data: Uint8Array) => {
    // 检查 PDF 查看器引用是否存在
    if (!pdfViewerRef.current) return;

    // 清空之前的 PDF 内容
    pdfViewerRef.current.innerHTML = "";
    // 获取 PDF 文档
    const pdf = await pdfjsLib.getDocument({ data }).promise;

    // 遍历 PDF 的每一页
    for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
      // 获取当前页
      const page = await pdf.getPage(pageNum);
      // 设置缩放比例
      const scale = 1.5;
      // 获取当前页的视口
      const viewport = page.getViewport({ scale });

      // 创建页面容器
      const pageDiv = document.createElement("div");
      pageDiv.style.position = "relative";
      pageDiv.style.width = `${viewport.width}px`;
      pageDiv.style.height = `${viewport.height}px`;
      pageDiv.style.margin = "0 auto 20px";
      pageDiv.style.border = "1px solid #ccc";

      // 创建 Canvas 用于渲染 PDF 背景
      const canvas = document.createElement("canvas");
      const context = canvas.getContext("2d");
      // 检查 Canvas 上下文是否存在
      if (!context) return;
      canvas.height = viewport.height;
      canvas.width = viewport.width;
      canvas.style.position = "absolute";
      pageDiv.appendChild(canvas);

      // 创建文本层容器
      const textLayerDiv = document.createElement("div");
      textLayerDiv.className = "textLayer";
      textLayerDiv.style.position = "absolute";
      textLayerDiv.style.left = "0";
      textLayerDiv.style.top = "0";
      textLayerDiv.style.right = "0";
      textLayerDiv.style.bottom = "0";
      pageDiv.appendChild(textLayerDiv);

      // 渲染 PDF 页面到 Canvas
      const renderContext = {
        canvasContext: context,
        viewport: viewport,
      };
      await page.render(renderContext).promise;

      // 提取并渲染文本层
      const textContent = await page.getTextContent();
      const textLayer = new pdfjsLib.TextLayer({
        textContentSource: textContent,
        container: textLayerDiv,
        viewport: viewport,
      });

      await textLayer.render();

      // 将页面容器添加到 PDF 查看器中
      pdfViewerRef.current.appendChild(pageDiv);
    }
  };

  /**
   * 复制选中文本的函数
   * @returns {void}
   */
  const handleCopyText = () => {
    // 检查是否有选中文本
    if (selectedText) {
      // 将选中文本复制到剪贴板
      navigator.clipboard
        .writeText(selectedText)
        .then(() => {
          // 提示复制成功
          alert("文本已复制到剪贴板！");
        })
        .catch((err) => {
          // 打印复制失败的错误信息
          console.error("复制失败:", err);
        });
    } else {
      // 提示用户先选择文本
      alert("请先选择文本！");
    }
  };

  /**
   * AI 解释选中文本的函数
   * @async
   * @returns {Promise<void>}
   */
  const handleExplainText = async () => {
    // 检查是否有选中文本
    if (selectedText) {
      try {
        // 调用 AI 解释 API
        const response = await fetch("/api/explain", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ text: selectedText }),
        });
        const data = await response.json();
        // 设置解释文本
        setExplanation(data.explanation);
      } catch (error) {
        // 打印 AI 解释失败的错误信息
        console.error("AI解释失败:", error);
        // 提示用户稍后重试
        alert("AI解释失败，请稍后重试！");
      }
    } else {
      // 提示用户先选择文本
      alert("请先选择文本！");
    }
  };

  /**
   * 监听文本选择事件的函数
   * @returns {void}
   */
  const handleTextSelection = () => {
    // 获取当前选中的文本
    const selection = window.getSelection();
    // 检查是否有选中文本
    if (selection && selection.toString().trim() !== "") {
      // 设置选中文本
      setSelectedText(selection.toString());

      // 获取选中文本的位置
      const range = selection.getRangeAt(0);
      const rect = range.getBoundingClientRect();

      // 设置悬浮框的位置，添加一些偏移量使其不会直接贴在文本上
      setFloatingBoxPosition({
        top: rect.bottom + window.scrollY + 10, // 向下偏移10px
        left: Math.max(rect.left + window.scrollX - 50, 10), // 向左偏移50px，但不小于10px
      });
      // 显示悬浮框
      setShowFloatingBox(true);
    } else {
      // 隐藏悬浮框
      setShowFloatingBox(false);
    }
  };

  /**
   * 点击页面其他地方时隐藏悬浮框的副作用函数
   */
  useEffect(() => {
    /**
     * 处理点击页面其他地方的函数
     * @param {MouseEvent} event - 鼠标事件
     * @returns {void}
     */
    const handleClickOutside = (event: MouseEvent) => {
      // 检查悬浮框引用是否存在，并且点击事件是否在悬浮框外部
      if (
        floatingBoxRef.current &&
        !floatingBoxRef.current.contains(event.target as Node)
      ) {
        // 隐藏悬浮框
        setShowFloatingBox(false);
      }
    };
    // 添加鼠标按下事件监听器
    document.addEventListener("mousedown", handleClickOutside);
    // 清理函数，移除事件监听器
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  return (
    <div className="container">
      {/* 左侧聊天框 */}
      <div className="chat-container">
        <div ref={chatContainerRef} className="chat-messages">
          {/* 遍历消息列表并渲染每个消息 */}
          {messages.map((message, index) => (
            <div key={index} className={`message-wrapper ${message.type}`}>
              <div className={`message-content ${message.type}`}>
                {message.type === "pdf" ? (
                  <div
                    onClick={() => message.pdfFile && loadPDF(message.pdfFile)}
                  >
                    <svg
                      viewBox="0 0 24 24"
                      width="20"
                      height="20"
                      fill="currentColor"
                    >
                      <path d="M8 16H16V18H8V16ZM8 12H16V14H8V12ZM14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2ZM18 20H6V4H13V9H18V20Z" />
                    </svg>
                    {message.content}
                  </div>
                ) : (
                  message.content
                )}
              </div>
            </div>
          ))}
        </div>

        <div className="input-container">
          <div className="input-wrapper">
            <input
              type="text"
              value={inputMessage}
              onChange={(e) => setInputMessage(e.target.value)}
              onKeyPress={(e) => e.key === "Enter" && handleSendMessage()}
              placeholder="输入消息..."
              className="message-input"
            />
            <input
              type="file"
              accept="application/pdf"
              style={{ display: "none" }}
              id="pdf-upload"
              onChange={(e) => {
                const file = e.target.files?.[0];
                if (file) handlePdfUpload(file);
              }}
            />
            <label htmlFor="pdf-upload" className="upload-button">
              <svg
                viewBox="0 0 24 24"
                width="24"
                height="24"
                fill="currentColor"
              >
                <path d="M6 2C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2H6ZM13 9V3.5L18.5 9H13Z" />
              </svg>
            </label>
          </div>
        </div>
      </div>

      {/* 右侧PDF预览 */}
      <div className="pdf-container">
        <div
          ref={pdfViewerRef}
          className="pdf-viewer"
          onMouseUp={handleTextSelection}
        >
          {showFloatingBox && (
            <div
              ref={floatingBoxRef}
              className="floating-toolbar"
              style={{
                top: floatingBoxPosition.top,
                left: floatingBoxPosition.left,
              }}
            >
              <button onClick={handleCopyText} className="toolbar-button copy">
                复制
              </button>
              <button
                onClick={handleExplainText}
                className="toolbar-button explain"
              >
                AI解释
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};
// 导出 PdfViewer 组件
export default PdfViewer;
